package com.chuangke.sinotrukbudgetapi.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.chuangke.admin.model.SysDept;
import com.chuangke.admin.model.SysDict;
import com.chuangke.admin.model.SysUser;
import com.chuangke.admin.service.SysDeptService;
import com.chuangke.admin.service.SysDictService;
import com.chuangke.admin.service.SysUserService;
import com.chuangke.common.constant.Masks;
import com.chuangke.common.enums.OrderStatusEnum;
import com.chuangke.core.exception.ChuangkeException;
import com.chuangke.core.id.IdManager;
import com.chuangke.core.validator.CkMapValidator;
import com.chuangke.sinotrukbudget.model.BudgetAccount;
import com.chuangke.sinotrukbudget.model.BudgetBpcPlan;
import com.chuangke.sinotrukbudget.sdao.BudgetBpcPlanDaoService;
import com.chuangke.sinotrukbudget.service.BudgetAccountService;
import com.chuangke.sinotrukbudget.service.BudgetBpcPlanService;
import com.chuangke.sinotrukbudget.util.BudgetUtil;
import com.chuangke.sinotrukbudgetapi.consts.POConsts;
import com.chuangke.sinotrukbudgetapi.service.BudgetApiService;
import com.chuangke.sinotrukbudgetapi.util.POUtil;
import com.chuangke.system.annotation.ApiLogDoc;

@Service
public class BudgetApiServiceImpl implements BudgetApiService {

	private static Logger logger = LoggerFactory.getLogger(BudgetApiServiceImpl.class);

	@Autowired
	private BudgetBpcPlanDaoService budgetBpcPlanDaoService;
	@Autowired
	private BudgetBpcPlanService budgetBpcPlanService;
	@Autowired
	private BudgetAccountService budgetAccountService;
	@Autowired
	private SysDictService sysDictService;
	@Autowired
	private SysDeptService sysDeptService;
	@Autowired
	private SysUserService sysUserService;

	@Override
	public Object receiveBudgetData(String budgetJson) {
		logger.info("接收BPCI002_A调用开始");
		JSONObject budgetJO = JSON.parseObject(budgetJson);
		JSONObject messageHeader = budgetJO.getJSONObject("MessageHeader");
		JSONObject requests = budgetJO.getJSONObject("Requests");

		JSONObject reponseBody = new JSONObject();
		try {
			validateMessageHeader(messageHeader);
			validateRequests(requests);

			List<BudgetBpcPlan> bbpList = parseBudgetData(messageHeader, requests);
			String auditUser = messageHeader.get("Submitter") == null ? "" : messageHeader.get("Submitter").toString();

			if (CollectionUtils.isEmpty(bbpList)) {
				throw new ChuangkeException("未接收到数据集");
			}

			budgetBpcPlanService.receiveBudgetData(bbpList);
			
			//20230218 add
			budgetBpcPlanService.handleHandleTime();

			// 获取可以参与流程的预算
			List<BudgetBpcPlan> folwBbpList = getCanStartFlowBudgetBpcPlan(bbpList);
			((BudgetApiService) AopContext.currentProxy()).startFlow(auditUser, folwBbpList);

			((BudgetApiService) AopContext.currentProxy()).initBudgetTimeToDict(bbpList);

			reponseBody.put("RETURN_CODE", HttpStatus.OK.value());
			reponseBody.put("RETURN_MESSAGE", "推送成功");
		} catch (Exception e) {
			logger.error("BPCI002_A接收异常", e);
			reponseBody.put("RETURN_CODE", HttpStatus.INTERNAL_SERVER_ERROR.value());
			reponseBody.put("RETURN_MESSAGE", "推送失败：" + ExceptionUtils.getMessage(e));
		}

		logger.info("接收BPCI002_A调用完成");
		logger.info("异步推送BPCI002_B接口，返回接收状态");
		String uuid = getUuid(budgetJO);
		JSONObject reponse = POUtil.createResponse(POConsts.REPONSE_INTERFACE_ID, uuid, reponseBody);

		((BudgetApiService) AopContext.currentProxy()).writeBackReceiveResult(reponse);

		return reponse;
	}

	@Async
	@ApiLogDoc(apiId = "BPCI002_B", apiName = "bpc预算反馈接口", reqType = "OUT")
	public String writeBackReceiveResult(JSONObject reponse) {
		logger.info("开始推送BPCI002_B接口，返回接收状态");
		String result = "数据推送成功";
		try {
			result = POUtil.send(POConsts.REPONSE_INTERFACE_ID, reponse);
		} catch (Exception e) {
			logger.error("推送BPCI002_B接口异常", e);
			result = e.getMessage();
		}
		logger.info("完成推送BPCI002_B接口，返回接收状态");
		return result;
	}

	@Scheduled(cron = "0 0 1,13,17 * * ? ")
	@Override
	public String writeBackAuditBudget() {
		List<BudgetBpcPlan> bbpList = budgetBpcPlanService.findAuditedAndUnWriteBackBudget();
		return ((BudgetApiService) AopContext.currentProxy()).writeBackAuditBudget(bbpList);
	}

	@ApiLogDoc(apiId = "BPCI003", apiName = "预算审批结果回写接口", reqType = "OUT")
	public String writeBackAuditBudget(List<BudgetBpcPlan> bbpList) {
		logger.info("开始推送BPCI003接口，返回审批数据");
		if (CollectionUtils.isEmpty(bbpList)) {
			logger.info("结束推送BPCI003接口，未找到可回写的数据");
			return "未找到可回写的数据";
		}
		String uuid = UUID.randomUUID().toString().replaceAll("-", "");
		JSONObject requestBody = convert(bbpList);
		JSONObject request = POUtil.createRequest(POConsts.AUDIT_OVER_INTERFACE_ID, uuid, requestBody);
		String result = null;
		try {
			result = POUtil.send(POConsts.AUDIT_OVER_INTERFACE_ID, request);

			List<String> bbpIdList = bbpList.stream().map(BudgetBpcPlan::getId).collect(Collectors.toList());
			String outUuid = getUuid(request);
			budgetBpcPlanDaoService
					.update(Wrappers.<BudgetBpcPlan>lambdaUpdate().set(BudgetBpcPlan::getOutStatus, Masks.BOOL_YES)
							.set(BudgetBpcPlan::getOutUuid, outUuid).in(BudgetBpcPlan::getId, bbpIdList));
		} catch (Exception e) {
			logger.error("推送BPCI003接口异常", e);
			result = e.getMessage();
		}
		logger.info("结束推送BPCI003接口");
		return result;
	}

	@Async
	public synchronized void initBudgetTimeToDict(List<BudgetBpcPlan> bbpList) {
		List<String> timeList = bbpList.stream().map(BudgetBpcPlan::gettTime).distinct()
				.filter(time -> !time.contains("INP")).collect(Collectors.toList());

		if (CollectionUtils.isEmpty(timeList)) {
			return;
		}

		Map<String, SysDict> dictMap = sysDictService.findMap("budget_time_month");
		Map<String, SysDict> nDictMap = dictMap.values().stream().collect(Collectors.toMap(SysDict::getCode, a -> a));
		for (String time : timeList) {
			if (nDictMap.keySet().contains(time)) {
				continue;
			}

			SysDict dict = new SysDict();
			dict.setName(time);
			dict.setCode(time);
			dict.setIsLastLevel("1");
			dict.setLevel(1);
			dict.setType("budget_time_month");
			dict.setParentId("0");

			sysDictService.save(dict);
		}

	}

	private String getUuid(JSONObject request) {
		return request.getJSONObject("MessageHeader").getString("UUID");
	}

	@Async
	public void startFlow(String auditUser, List<BudgetBpcPlan> bbpList) {
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		budgetBpcPlanService.startFlowAndSubmit(auditUser, bbpList);
	}

	private JSONObject convert(List<BudgetBpcPlan> bbpList) {
		JSONObject requestBody = new JSONObject();
		BudgetBpcPlan bbpheader = bbpList.get(0);
		requestBody.put("Submitter", bbpheader.getSubmitter());
		requestBody.put("Remark1", bbpheader.getRemark1());
		requestBody.put("Remark2", bbpheader.getRemark2());
		requestBody.put("Remark3", bbpheader.getRemark3());
		requestBody.put("Remark4", bbpheader.getRemark4());
		requestBody.put("Remark5", bbpheader.getRemark5());
		requestBody.put("Remark6", bbpheader.getRemark6());

		JSONArray rows = new JSONArray();
		bbpList.forEach(bbp -> {
			JSONObject jsonObject = new JSONObject();

			jsonObject.put("T_ENTITY", bbp.gettEntity());
			jsonObject.put("T_CATEGORY", bbp.gettCategory());
			jsonObject.put("T_AUDIT", bbp.gettAudit());
			jsonObject.put("T_ACCOUNT", bbp.gettAccount());
			jsonObject.put("T_TIME", bbp.gettTime());
			jsonObject.put("T_FLOW", bbp.gettFlow());
			jsonObject.put("T_FLOW1", bbp.gettFlow1());
			jsonObject.put("T_FLOW2", bbp.gettFlow2());
			jsonObject.put("T_FLOW3", bbp.gettFlow3());
			jsonObject.put("T_FLOW4", bbp.gettFlow4());
			jsonObject.put("T_FLOW5", bbp.gettFlow5());
			jsonObject.put("T_FLOW6", bbp.gettFlow6());
			jsonObject.put("T_OBJECT", bbp.gettObject());
			jsonObject.put("SignData", bbp.getSigndata().toString());
			jsonObject.put("SCOMMENT", bbp.getScomment());
			jsonObject.put("Remark1", bbp.getRowRemark1());
			jsonObject.put("Remark2", bbp.getRowRemark2());
			jsonObject.put("Remark3", bbp.getRowRemark3());
			jsonObject.put("Remark4", bbp.getRowRemark4());

			rows.add(jsonObject);
		});

		requestBody.put("Rows_1", rows);
		return requestBody;
	}

	private String handleData(Object value) {
		return value == null ? "" : value.toString().trim();
	}
	
	private String getDeptType(JSONObject requests) {
		String submitter = handleData(requests.get("Submitter")) ;
		SysUser submitterUser = sysUserService.findByName(submitter) ;
		if(submitterUser == null || StrUtil.isBlank(submitterUser.getDeptId())) {
			return "" ;
		}
		
		Map<String, SysDept> deptMap = sysDeptService.findMap();
		SysDept dept = deptMap.get(submitterUser.getDeptId()) ;
		
		String deptType = dept == null ? "" : dept.getDeptType() ;
		if(StrUtil.isBlank(deptType)) {
			logger.error("提交人{}所属组织机为{}，在组织机构中未配置组织机构类型", submitter,submitterUser.getDeptId());
		}
		
		return deptType ;
	}

	private List<BudgetBpcPlan> parseBudgetData(JSONObject messageHeader, JSONObject requests) {
		JSONArray rows = requests.getJSONArray("Rows_1");
		List<BudgetBpcPlan> list = new ArrayList<>();

		if (rows == null || rows.size() == 0) {
			return list;
		}

		Map<String, BudgetAccount> accountMap = budgetAccountService.findMap();

		String deptType = getDeptType(requests) ;
		
		for (int i = 0; i < rows.size(); i++) {
			JSONObject jsonObject = rows.getJSONObject(i);

			validateRow(jsonObject);

			BudgetBpcPlan bbp = new BudgetBpcPlan();
			bbp.setId(IdManager.nextId());

			bbp.setSubmitter(handleData(requests.get("Submitter")));
			bbp.setRemark1(handleData(requests.get("Remark1")));
			bbp.setRemark2(handleData(requests.get("Remark2")));
			bbp.setRemark3(handleData(requests.get("Remark3")));
			bbp.setRemark4(handleData(requests.get("Remark4")));
			bbp.setRemark5(handleData(requests.get("Remark5")));
			bbp.setRemark6(handleData(requests.get("Remark6")));

			bbp.settEntity(handleData(jsonObject.get("T_ENTITY")));
			bbp.settCategory(handleData(jsonObject.get("T_CATEGORY")));
			bbp.settAudit(handleData(jsonObject.get("T_AUDIT")));
			bbp.settAccount(handleData(jsonObject.get("T_ACCOUNT")));
			bbp.settTime(handleData(jsonObject.get("T_TIME")));
			bbp.setHandleTime(bbp.gettTime());
			bbp.settFlow(handleData(jsonObject.get("T_FLOW")));
			bbp.settFlow1(handleData(jsonObject.get("T_FLOW1")));
			bbp.settFlow2(handleData(jsonObject.get("T_FLOW2")));
			bbp.settFlow3(handleData(jsonObject.get("T_FLOW3")));
			bbp.settFlow4(handleData(jsonObject.get("T_FLOW4")));
			bbp.settFlow5(handleData(jsonObject.get("T_FLOW5")));
			bbp.settFlow6(handleData(jsonObject.get("T_FLOW6")));
			bbp.settObject(handleData(jsonObject.get("T_OBJECT")));

			String signData = handleData(jsonObject.get("SignData"));
			signData = StrUtil.isBlank(signData) ? "0" : signData;
			signData = signData.endsWith("-") ? "-" + signData.substring(0, signData.length() - 1) : signData;
			BigDecimal data = new BigDecimal(signData);
			bbp.setSigndata(data);

			bbp.setScomment(handleData(jsonObject.get("SCOMMENT")));
			bbp.setRowRemark1(handleData(jsonObject.get("Remark1")));
			bbp.setRowRemark2(handleData(jsonObject.get("Remark2")));
			bbp.setRowRemark3(handleData(jsonObject.get("Remark3")));
			bbp.setRowRemark4(handleData(jsonObject.get("Remark4")));

			bbp.setStatus(OrderStatusEnum.EDIT.getCode());
			bbp.setBisType("budget");
			bbp.setDeptType(deptType);
			bbp.setInUuid(handleData(messageHeader.get("UUID")));
			bbp.setInStatus(Masks.BOOL_YES);
			bbp.setOutStatus(Masks.BOOL_NO);

			BudgetAccount ba = accountMap.get(bbp.gettAccount());
			bbp.setAccountType(ba == null ? BudgetAccount.ACCOUNT_TYPE_CW : ba.getType());

			list.add(bbp);
		}

		return list;
	}

	private void validateMessageHeader(JSONObject messageHeader) {
		CkMapValidator validator = CkMapValidator.getInstance(messageHeader);
		validator.notEmpty("Interface_ID", "接口编码Interface_ID不能为空");
		validator.notEmpty("UUID", "数据唯一值UUID不能为空");
		validator.notEmpty("Sender", "发送方系统名称不能为空");
		validator.notEmpty("Receiver", "接收方系统名称不能为空");
		validator.notEmpty("SendDate", "发送日期[格式：20200804]不能为空");
		validator.notEmpty("SendTime", "发送时间[例如：080808]不能为空");
	}

	private void validateRequests(JSONObject requests) {
		CkMapValidator validator = CkMapValidator.getInstance(requests);
		validator.notEmpty("Submitter", "上报者(工号)不能为空");
	}

	private void validateRow(JSONObject row) {
		CkMapValidator validator = CkMapValidator.getInstance(row);
		validator.notEmpty("T_ENTITY", "T_ENTITY预算组织不能为空");
		validator.notEmpty("T_CATEGORY", "T_CATEGORY预算版本不能为空");
		validator.notEmpty("T_AUDIT", "T_AUDIT审计线索不能为空");
		validator.notEmpty("T_ACCOUNT", "T_ACCOUNT预算科目不能为空");
		validator.notEmpty("T_TIME", "T_TIME预算期间不能为空");
		validator.notEmpty("T_FLOW", "T_FLOW功能范围不能为空");
		validator.notEmpty("T_FLOW1", "T_FLOW1预算事项不能为空");
		validator.notEmpty("T_FLOW2", "T_FLOW2预算产品不能为空");
		validator.notEmpty("T_FLOW3", "T_FLOW3关联方不能为空");
		validator.notEmpty("T_FLOW4", "T_FLOW4业务项目不能为空");
		validator.notEmpty("T_FLOW5", "T_FLOW5预留不能为空");
		validator.notEmpty("T_FLOW6", "T_FLOW6预留不能为空");
		validator.notEmpty("T_OBJECT", "T_OBJECT预留不能为空");
	}

	/**
	 * 获取可以参与流程的预算
	 * 
	 * @param bbpList
	 * @return
	 */
	private List<BudgetBpcPlan> getCanStartFlowBudgetBpcPlan(List<BudgetBpcPlan> bbpList) {
		return bbpList.stream().filter(BudgetUtil::isInFlow).collect(Collectors.toList());
	}

}
